#pragma once
#include <vector>
#include <memory>
#include "../parallel/thread.hpp"

namespace umtl
{
	//////////////////////////////////////////////////////////////////////////
	//

	namespace unit_pool_thread_model
	{
		struct thread_safe { typedef parallel::critical_section::section_locker locker; };
		struct none_thread_safe { typedef struct none_locker { none_locker() {} none_locker( parallel::critical_section & ) {} } locker; };
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename DataType, typename ThreadModel >
	class unit_pool
	{
	public:

		typedef std::vector< void * > Container;
		typedef std::vector< Container > ContainerMap;

		void * pop(size_t n) {

			if( n == 0 )
				return 0;
			
			typename ThreadModel::locker l( get_critical_section() );

			for( size_t i=container_.size(); i<n; ++i )
				container_.push_back( Container() );

			Container & container = container_[n-1];

			if( container.empty() )
				return new char[ sizeof(DataType) * n ];

			void * p = container.back();

			container.pop_back();

			return p;
		}

		void push(void * p, size_t n) {

			if( p && n )
			{
				typename ThreadModel::locker l( get_critical_section() );

				for( size_t i=container_.size(); i<n; ++i )
					container_.push_back( Container() );

				container_[n-1].push_back(p);
			}
		}

		unit_pool() {};

		virtual ~unit_pool() {

			for( ContainerMap::iterator i = container_.begin(); i != container_.end(); ++i )
			{
				for( Container::iterator j = i->begin(); j != i->end(); ++j )
					delete[] ((char*)(*j));
			}

			container_.clear();
		}

	private:
		ContainerMap container_;
		//smart_ptr< parallel::critical_section > ct_;
		std::tr1::shared_ptr< parallel::critical_section > ct_;

		unit_pool( unit_pool const & other );
		void operator=( unit_pool const & other );

		inline parallel::critical_section & get_critical_section()
		{
			if( ct_ == nullptr )
			{
				static parallel::critical_section ct;
				typename ThreadModel::locker l(ct);
				ct_.reset( new parallel::critical_section() );
			}

			return *ct_;
		}
	};

	//////////////////////////////////////////////////////////////////////////
}